// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

export component SliderBase {
    in property <bool> enabled <=> touch-area.enabled;
    in property <float> minimum: 0;
    in property <float> maximum: 100;
    in property <float> step: 1;
    in property <Orientation> orientation;
    in property <length> handle-x;
    in property <length> handle-y;
    in property <length> handle-width;
    in property <length> handle-height;
    out property <bool> pressed <=> touch-area.enabled;
    out property <bool> has-hover <=> touch-area.has-hover;
    out property <bool> vertical: root.orientation == Orientation.vertical;
    out property <bool> has-focus <=> focus-scope.has-focus;
    out property <bool> handle-has-hover: touch-area.mouse-x >= root.handle-x && touch-area.mouse-x <= root.handle-x + root.handle-width &&
        touch-area.mouse-y >= root.handle-y && touch-area.mouse-y <= root.handle-y + root.handle-height;
    out property <bool> handle-pressed;
    in-out property <float> value: minimum;

    callback changed(value: float);
    callback released(value: float);

    private property <length> ref-size: !root.vertical ? root.handle-width : root.handle-height;

    forward-focus: focus-scope;

    touch-area := TouchArea {
        property <float> pressed-value;

        width: 100%;
        height: 100%;

        pointer-event(event) => {
            if (event.button != PointerEventButton.left) {
                return;
            }

            if (event.kind == PointerEventKind.up) {
                if (root.handle-pressed) {
                    root.released(root.value);
                }
                root.handle-pressed = false;
                return;
            }


            if (!root.handle-has-hover) {
                root.set-value((!root.vertical ? root.size-to-value(touch-area.mouse-x, root.width) :
                    root.size-to-value(root.height - touch-area.mouse-y, root.height)) + root.minimum);
            }

            self.pressed-value = value;
            focus-scope.focus();
            root.handle-pressed = true;
        }

        moved => {
            if (!self.enabled) {
                return;
            }

            root.set-value(self.pressed-value + (!vertical ? root.size-to-value(touch-area.mouse-x - touch-area.pressed-x, root.width - root.ref-size) :
                root.size-to-value(touch-area.pressed-y - touch-area.mouse-y, root.height - root.ref-size))
            );
        }
    }

    focus-scope := FocusScope {
        x: 0;
        y: 0;
        width: 0;
        height: 0;
        enabled: root.enabled;

        key-pressed(event) => {
            if (!self.enabled || root.step <= 0) {
                return reject;
            }

            if ((!vertical && event.text == Key.RightArrow) || (vertical && event.text == Key.UpArrow)) {
                root.increment();
                return accept;
            } else if ((!vertical && event.text == Key.LeftArrow) || (vertical && event.text == Key.DownArrow)) {
                root.decrement();
                return accept;
            } else if (event.text == Key.Home) {
                root.set-value(root.minimum);
                return accept;
            } else if (event.text == Key.End) {
                root.set-value(root.maximum);
                return accept;
            }

            reject
        }

        key-released(event) => {
            if (!self.enabled) {
                return reject;
            }
            if (!vertical && event.text == Key.RightArrow) || (vertical && event.text == Key.DownArrow)
               || (!vertical && event.text == Key.LeftArrow) || (vertical && event.text == Key.UpArrow)
               || event.text == Key.Home || event.text == Key.End {
                root.released(root.value);
            }
            return accept;
        }
    }

    pure function size-to-value(size: length, range: length) -> float {
        size * (root.maximum - root.minimum) / range;
    }

    public function set-value(value: float) {
        if (root.value == value) {
            return;
        }

        root.value = max(root.minimum, min(root.maximum, value));
        root.changed(root.value);
    }

    public function increment() {
        root.set-value(root.value + root.step);
    }

    public function decrement() {
        root.set-value(root.value - root.step);
    }
}
